% Created 2022-05-19 周四 13:20
% Intended LaTeX compiler: xelatex
\documentclass[a4paper]{article}
                 \usepackage{graphicx}
                 \usepackage{xcolor}
                 \usepackage{xeCJK}
                 \usepackage{fixltx2e}
                 \usepackage{longtable}
                 \usepackage{float}        
                 \usepackage{tikz}         
                 \usepackage{wrapfig}      
                 \usepackage{latexsym,amssymb,amsmath}
                 \usepackage{textcomp}
                 \usepackage{bicaption}
                 \usepackage{listings}     
                \usepackage{marvosym}     
                \usepackage{textcomp}     
                \usepackage{latexsym}     
                \usepackage{natbib}
                \usepackage{indentfirst}
		\usepackage{hyperref}
                \usepackage{geometry}
                \usepackage{minted}
                \setCJKmainfont{宋体}
                \setlength{\parindent}{2em}
                \usepackage{color}
                \usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[R]{\footnotesize{《Android 物联网应用程序开发》 广西职业师范学院实验教学指导书}}
\geometry{a4paper,left=2.5cm,right=2.5cm,top=2cm,bottom=2cm}
\setCJKmainfont{仿宋}
\setCJKsansfont{Microsoft YaHei}
\setCJKmonofont{Microsoft YaHei}

            \lstset{
  columns=fixed,       
  breaklines=true, breakautoindent=true,breakindent=4em,
  numbers=left,                                        % 在左侧显示行号
  frame=none,                                          % 不显示背景边框
  backgroundcolor=\color[RGB]{245,245,244},            % 设定背景颜色
  keywordstyle=\color[RGB]{40,40,255},                 % 设定关键字颜色
  numberstyle=\footnotesize\color{darkgray},           % 设定行号格式
  commentstyle=\it\color[RGB]{0,96,96},                % 设置代码注释的格式
  stringstyle=\rmfamily\slshape\color[RGB]{128,0,0},   % 设置字符串格式
  showstringspaces=false,                              % 不显示字符串中的空格
  language=java,                                        % 设置语言
}

\usemintedstyle{emacs}
    \setminted{%
        autogobble,% automatically remove the spaces to the left of the code
        linenos=true,% show line numbers
        fontsize=\small,% smaller font size
        % escapeinside=@@,% insert latex code between @ and @
        frame=lines,% frame styles: none or lines
        % frame=none,
        numbers=none,% show line numbers on: left, right, both or none
        mathescape=true,% insert math code
	breaklines=true,
    }%
    \setmintedinline{%
        fontsize=\smallsize,
    }%
\date{\today}
\title{实验十: Content Provider 实训}
\hypersetup{
 pdfauthor={},
 pdftitle={实验十: Content Provider 实训},
 pdfkeywords={},
 pdfsubject={},
 pdfcreator={Emacs 27.1 (Org mode 9.3.8)}, 
 pdflang={English}}
\begin{document}

\maketitle
\tableofcontents

\newpage

\section{实验目的}
\label{sec:orgecb27e3}
\begin{itemize}
\item 掌握 Content Provider 的使用方式
\end{itemize}
\section{实验设备和器件}
\label{sec:org4af6210}
\begin{itemize}
\item PC
\item 内存要求 8G 以上
\end{itemize}
\section{实验内容}
\label{sec:org70c2887}
\subsection{实训步骤}
\label{sec:org3ab11bf}
\subsubsection{准备0:}
\label{sec:orgf269ce3}
\begin{itemize}
\item 在手机应用程序里使用contacts应用添加几个联系人
\item 确保在数据库中能够查询到这几个联系人
\end{itemize}
\subsubsection{步骤1:应用界面布局}
\label{sec:org339c5b6}
\begin{itemize}
\item 添加一个按钮，按钮下方添加ListView组件用以显示联系人
\item 参考界面
\begin{center}
\includegraphics[width=0.5\textwidth]{img/3.jpg}
\end{center}

\item 关键程序  
\lstset{language=XML,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ContentTest">
  <Button
      android:id="@+id/getcontacts"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Button"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintTop_toTopOf="parent"/>
  <ListView
      android:id="@+id/contacts"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintTop_toBottomOf="@id/getcontacts"/>
</android.support.constraint.ConstraintLayout>
\end{lstlisting}
\end{itemize}
\subsubsection{步骤2:添加主要成员}
\label{sec:orgc61f7d2}
\begin{itemize}
\item 在MainActivity.java添加主要成员
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
Button btn;
    ListView listView;
    List<Phone> list = new ArrayList<Phone>(); //存放带phone对象的列表
    List<String> showList = new ArrayList<String>(); //存放最后展示的字符串列表
\end{lstlisting}
\end{itemize}
\subsubsection{步骤2:获取联系人读取权限(静态申请)}
\label{sec:org7668623}
\begin{itemize}
\item 在AndroidManifest.xml添加如下权限
\lstset{language=XML,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
<uses-permission android:name="android.permission.READ_CONTACTS" />
\end{lstlisting}
\end{itemize}
\subsubsection{步骤3:动态申请权限}
\label{sec:orgaa1b856}
\begin{itemize}
\item android6.0 以上版本不仅要在AndroidManifest.xml中静态申请，还需要在代码中进行动态申请。
\item 动态申请的流程为：发起申请和申请结果。
\item 发起申请:
主要使用requestPermissions(permission名字的数组,整型的自定义requescode)方法,
什么时候进行动态申请呢？版本大于等于6.0且当前页面没有这个权限。request code
为自定义数值，在本例中定义了一个常量
int--PERMISSIONS\_REQUEST\_READ\_CONTACTS，这个是为了在查看申请结果
时判定用的。
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
    && checkSelfPermission(Manifest.permission.READ_CONTACTS) 
    != PackageManager.PERMISSION_GRANTED) 
{
    //发起申请
    requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, 
		       PERMISSIONS_REQUEST_READ_CONTACTS);
} else {
    //低版本直接运行，跳过动态申请。
    readContacts();
}
\end{lstlisting}
\item 申请结果处理: 
重写onRequsetPermissionResult(int requestCode,String[] permissions,int[] grantResults)方法。
在此返回的requestCode是发送申请中自定义的requestCode，permissions
String数组也是发送申请中的，grantResults返回的有两种PackageManager.PERMISSION\_GRANTED
和PackageManager.PERMISSION\_DENIED
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
@Override
public void onRequestPermissionsResult(int requestCode,  
				       String[] permissions,  
				       int[] grantResults) 
  {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
      if(requestCode == PERMISSIONS_REQUEST_READ_CONTACTS)
      {
	  if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
	      // 申请权限通过
	      readContacts();
	} else 
	  {
	      Toast.makeText(this, "只有通过了权限申请才能查看", Toast.LENGTH_SHORT).show();
	  }
    }
}
\end{lstlisting}
\end{itemize}

\subsubsection{步骤4:使用ContentResolver获取联系人数据}
\label{sec:org52ef770}
主要使用ContentResolver进行操作,使用游标来对ContentResolver获取的数据进行操作。
\begin{itemize}
\item 创建Phone pojo类
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
class Phone{
    private String name;
    private String number;
    public Phone(String name, String number) {
	this.name = name;
	this.number = number;
    }
    public String getName() {
	return name;
    }
    public void setName(String name) {
	this.name = name;
    }
    public String getNumber() {
	return number;
    }
    public void setNumber(String number) {
	this.number = number;
    }
}
\end{lstlisting}
\item 获得ContentResolver
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
ContentResolver resolver = getContentResolver();
\end{lstlisting}
\item 确定uri和要查询的字段
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
Uri uri= ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String[] strs={ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
	       ContactsContract.CommonDataKinds.Phone.NUMBER};

\end{lstlisting}
\item 执行查询并将结果遍历展现在ListView中和存储到本地的数据库中  
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
Cursor cursor=resolver.query(uri,strs,null,null,null);
while(cursor.moveToNext()){
    String name=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
    String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
    list.add(new Phone(name,number));
}
\end{lstlisting}
\end{itemize}
\subsubsection{步骤5:将获取的数据显示到ListView列表}
\label{sec:orgc1c5918}
\begin{itemize}
\item 数据保存在list列表，将数据取出，整合成字符串列表，通过适配器adapter显示到ListView组件
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
//清空list
showList.clear();
for(int i = 0; i < list.size(); i++)
{
    Phone phone = (Phone)(list.get(i));
    String str = phone.getName() + " \t" + phone.getNumber();
    showList.add(str);
}
//将结果显示到listView
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>
    (getApplicationContext(),android.R.layout.simple_list_item_1,showList);
listView.setAdapter(arrayAdapter);
\end{lstlisting}
\end{itemize}
\end{document}